
import copy
import json
import datetime as dt
import time

import joblib
import numpy as np
import pandas as pd
import functools
from utils_DP import DouglasPeuker


def time_log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            start_time = time.time()
            time_str = time.strftime(
                u"%Y-%m-%d %X", time.localtime(start_time))
            print(' %s: %s Starting!' % (time_str, text))
            ret = func(*args, **kw)
            end_time = time.time()
            time_str = time.strftime(
                u"%Y-%m-%d %X", time.localtime(end_time))
            print(' %s: %s Finish!\n' % (time_str, text))
            print(' 耗时: %s 秒!\n' % (end_time - start_time))
            return ret
        return wrapper
    return decorator


def cal_dist(df=None):
    dfp = copy.deepcopy(df)
    dfp.columns = ['lat_1', 'lng_1', 'lat_2', 'lng_2']
    ra = 6378140  # radius of equator: meter
    rb = 6356755  # radius of polar: meter
    flatten = (ra - rb) / ra  # Partial rate of the earth
    dfp['radLatA'] = np.radians(dfp['lat_1'])
    dfp['radLonA'] = np.radians(dfp['lng_1'])
    dfp['radLatB'] = np.radians(dfp['lat_2'])
    dfp['radLonB'] = np.radians(dfp['lng_2'])
    dfp['pA'] = np.arctan(np.tan(dfp['radLatA']) * rb / ra)
    dfp['pB'] = np.arctan(np.tan(dfp['radLatB']) * rb / ra)
    dfp['x'] = np.arccos(np.sin(dfp['pA']) * np.sin(dfp['pB']) + np.cos(dfp['pA']) * np.cos(dfp['pB']) * np.cos(dfp['radLonA'] - dfp['radLonB']))
    dfp['c1'] = (np.sin(dfp['x']) - dfp['x']) * (np.sin(dfp['pA']) + np.sin(dfp['pB'])) **2 / np.cos(dfp['x'] / 2) **2
    dfp['c2'] = (np.sin(dfp['x']) + dfp['x']) * (np.sin(dfp['pA']) - np.sin(dfp['pB'])) **2 / np.sin(dfp['x'] / 2) **2
    dfp['dr'] = flatten / 8 * (dfp['c1'] - dfp['c2'])
    dfp['distance'] = ra * (dfp['x'] + dfp['dr'])
    return dfp['distance']

# 自定义分箱
def define_cut_serial(ser=None, cut_value=[]):
    df = ser.to_frame('col')
    df['cut_serial'] = float('nan')
    for i, v in enumerate(cut_value):
        if i == 0:
            df['cut_serial'].mask(df['col'] <= cut_value[0], 0, inplace=True)
        elif i >= 1:
            df['cut_serial'].mask((df['col'] > cut_value[i - 1]) & (df['col'] <= cut_value[i]), i, inplace=True)

    df['cut_serial'].mask(df['col'] > cut_value[-1], len(cut_value), inplace=True)
    return df['cut_serial']

# 供gaode_2_points_pre函数使用
def st_2_df(st=''):
    lis = st.split(';')
    new_st = ''
    for li in lis:
        new_st = new_st + '[' + li + '],'
    new_st = '[' + new_st[0:-1] + ']'
    new_lis = json.loads(new_st)
    df = pd.DataFrame(new_lis, columns=['locationLng', 'locationLat'])
    return df

# 将gaode返回的路径点信息由字符串转为df
def gaode_2_points_pre(rn):
    # 高德
    start_point = rn['route']['origin']
    end_point = rn['route']['destination']
    steps = rn['route']['paths'][0]['steps']
    #
    d = pd.DataFrame()
    for h, step in enumerate(steps):
        step_df = st_2_df(step['polyline'])
        step_df['duan'] = h
        d = pd.concat([d, step_df])

    start_point_df = st_2_df(start_point)
    end_point_df = st_2_df(end_point)
    e =  pd.concat([start_point_df, d, end_point_df], sort=True)
    points_pre = e.reset_index(drop=False).rename(columns={'index': 'induan'})
    points_pre['serial'] = range(points_pre.shape[0])
    #
    s = points_pre[['locationLat','locationLng']].shift(1, axis=0).rename(columns={'locationLat':'locationLat_1', 'locationLng':'locationLng_1'})
    points_pre = pd.concat([points_pre, s], axis=1)
    points_pre['run'] = cal_dist(points_pre[['locationLat', 'locationLng', 'locationLat_1', 'locationLng_1']])
    points_pre['run'].fillna(0, inplace=True)
    points_pre['cumsum'] = points_pre['run'].cumsum()
    points_pre['cumsum'].fillna(0, inplace=True)
    return points_pre

# 对路径点进行稀疏化
def sparse_points(points_pre):
    points = copy.deepcopy(points_pre)
    d = DouglasPeuker(1000)
    after_sample_list = d.main(points)
    after_sample_df = pd.DataFrame(after_sample_list , columns = ['locationLng','locationLat'])
    after_sample_df['cut_serial'] = 1
    points = points.merge(after_sample_df , on = ['locationLng','locationLat'] , how = 'left')
    
    return points

# 单位电量可行驶里程的预测函数
def pred_unit_mi(input_1, input_2):
    # para
    one = pd.read_csv(r'one.csv', low_memory=False, encoding='utf-8-sig')
    one['vin'] = 33
    one['start_leijilicheng'] = 6800
    clw = json.loads(input_1)
    rn = json.loads(input_2)
    # 车辆初始状态特征
    initial_state_feature = ['vin', 'start_leijilicheng', 'start_soc', 'distance', 'start_longitude', 'start_latitude','end_longitude',
                        'end_latitude', 'report_month', 'week_day', 'hour']

    shift_feature_list = ['start_soc', 'end_soc', 'start_longitude', 'start_latitude', 'end_longitude', 'end_latitude', '...']
    lag_features = []
    for f in shift_feature_list:
        for k in range(1,11):
            lag_features.append('last_' + str(k) + '_' + f)
    origin_features = initial_state_feature + lag_features
    categorical_features = ['vin','report_month','week_day','hour']


    # 修改one
    one['start_soc'] = clw['start_soc']
    one['start_report_time'] = dt.datetime.now()
    one['week_day'] = one.apply(lambda x: x['start_report_time'].weekday(), axis=1)
    one['hour'] = one.apply(lambda x: x['start_report_time'].hour, axis=1)
    one['report_month'] = one.apply(lambda x: x['start_report_time'].month, axis=1)
    one['distance'] = float(rn['route']['paths'][0]['distance'])
    #
    start_point = rn['route']['origin']
    end_point = rn['route']['destination']
    one['start_longitude'] = float(start_point.split(',')[0])
    one['start_latitude'] = float(start_point.split(',')[1])
    one['end_longitude'] = float(end_point.split(',')[0])
    one['end_latitude'] = float(end_point.split(',')[1])
    
    one = one[one['last_10_jueyuanzhuzhi_median'].notna()].reset_index(drop=True)

    # 预测
    search = joblib.load("")
    ### 预测结果输出
    result_df = copy.deepcopy(one)
    result_df['predict_unit_soc_mileage'] = search.predict(result_df[origin_features]) * 1000.0
    return result_df.loc[0, 'predict_unit_soc_mileage']
